package com.line.service;

import com.line.config.cons.BasicCons;
import com.line.config.cons.CodeCons;
import com.line.config.cons.RedisCons;
import com.line.config.exception.GlobalResponseException;
import com.line.model.entity.AuthAppEntity;
import com.line.model.pojo.AccessTokenPojo;
import com.line.model.pojo.PrincipalPojo;
import com.line.service.api.IAuthService;
import com.line.tools.util.HttpUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service(value = "authService")
public class AuthService implements IAuthService {

	private Logger log = LoggerFactory.getLogger(AuthService.class);

	@Autowired
	private RedisTemplate<String, Object> redisTemplate; // 缓存交互类

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response) {
		String uri = request.getRequestURI();
		// 首先，判断token是否有效
		String headAuth = request.getHeader("Authorization");
		String accessToken = null;
		if (!StringUtils.isBlank(headAuth) && headAuth.toLowerCase().startsWith("bearer")) {
			// 从头部获取access_token
			accessToken = headAuth.substring("bearer".length() + 1, headAuth.length());
		} else {
			// 从参数获取access_token
			accessToken = request.getParameter("access_token");
		}
		if (StringUtils.isBlank(accessToken)) {
			throw new GlobalResponseException(CodeCons.AUTH_VALID_TOKEN);
		}

		// 从redis获取授权信息
		String redisKey = RedisCons.AUTH_PRINCIPAL + accessToken.replaceAll("-", "");// 替换“-”符号，用于redis获取相关信息
		PrincipalPojo principalPojo = (PrincipalPojo) redisTemplate.opsForValue().get(redisKey);
		if (principalPojo == null) {
			throw new GlobalResponseException(CodeCons.AUTH_TOKEN_EXPIRES);
		}

		// 线程缓存
		PrincipalPojo localPojo = BasicCons.AUTH_THREADLOCAL.get();
		if (localPojo != null) {
			if (!principalPojo.getAccessToken().equals(localPojo.getAccessToken())) {
				BasicCons.AUTH_THREADLOCAL.remove();
			}
		}
		BasicCons.AUTH_THREADLOCAL.set(principalPojo);

		// 校验IP是否在白名单
		String remoteAddress = HttpUtils.getIpAddress(request);
		if (HttpUtils.matchIp(principalPojo.getIpWhite(), remoteAddress)) {
			throw new GlobalResponseException(CodeCons.AUTH_IP_LIMIT);
		}

		// 校验授权帐号是否有权限访问该资源
		String path = request.getRequestURI();
		path = path.replaceAll("/", ":");
		path = path.substring(1, path.length());
		String resKey = RedisCons.AUTH_RES + path;
		Set<String> setClient = (Set<String>) redisTemplate.opsForValue().get(resKey);
		if (setClient == null || !setClient.contains(principalPojo.getAppid())) {
			throw new GlobalResponseException(CodeCons.AUTH_TOKEN_NO_RES);
		}
		return true;
	}

	@Override
	public AccessTokenPojo token(String grantType, String appid, String secret) {
		if (StringUtils.isBlank(grantType) || StringUtils.isBlank(appid) || StringUtils.isBlank(secret)) {
			throw new GlobalResponseException(CodeCons.ERROR_PARAM);
		}
		// 校验appid与secret是否正确
		String redisKey = RedisCons.AUTH_APPID + appid;
		AuthAppEntity appEntity = (AuthAppEntity) redisTemplate.opsForValue().get(redisKey);
		if (appEntity == null || !appEntity.getSecret().equalsIgnoreCase(secret)) {
			throw new GlobalResponseException(CodeCons.AUTH_APPID_ERROR);
		}
		redisKey = RedisCons.AUTH_TOKEN + appid;
		AccessTokenPojo tokenPojo = (AccessTokenPojo) redisTemplate.opsForValue().get(redisKey);
		// 生成token
		if (grantType.equals(BasicCons.AUTH_GRANT_CLIENT)) { // 客户端授权
			if (tokenPojo == null) {
				tokenPojo = new AccessTokenPojo();
				String accessToken = UUID.randomUUID().toString();
				tokenPojo.setAccess_token(accessToken);
				tokenPojo.setExpires_in((long) appEntity.getAccessTokenExpires());

				// 将新增的token存储根据当前授权帐号存储
				redisTemplate.opsForValue().set(redisKey, tokenPojo);
				redisTemplate.expire(redisKey, appEntity.getAccessTokenExpires(), TimeUnit.SECONDS);

				PrincipalPojo principalPojo = new PrincipalPojo();
				principalPojo.setAccessToken(accessToken);
				principalPojo.setAccessTokenExpires(appEntity.getAccessTokenExpires());
				principalPojo.setRefreshTokenExpires(appEntity.getRefreshTokenExpires());
				principalPojo.setAppid(appid);
				principalPojo.setSecret(secret);
				principalPojo.setGrantType(grantType);

				redisKey = RedisCons.AUTH_PRINCIPAL + accessToken.replaceAll("-", "");
				redisTemplate.opsForValue().set(redisKey, principalPojo);
				redisTemplate.expire(redisKey, appEntity.getAccessTokenExpires(), TimeUnit.SECONDS);
			} else {
				tokenPojo.setExpires_in(redisTemplate.getExpire(redisKey));
			}
		}
		return tokenPojo;
	}

}
